package com.haodf.skeleton

import android.content.Context
import android.graphics.*
import android.graphics.drawable.*
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.haodf.skeleton.anim.BaiJvGuoXiLoadingAnimtor
import com.haodf.skeleton.anim.ILoadingAnimtor
import com.haodf.skeleton.anim.RuoYinRuoXianLoadingAnimtor

/**
 * @author xiaxiao
 * @date 2023/4/26.
 * 负责绘制
 */
class SkeletonView(context: Context?) : View(context) {

    companion object {
        private const val PAD_VIEWGROUP = 4
        private const val PAD_VIEW = 10
    }

    private var pad = PAD_VIEW
    private val radius = 10f
    var target: View? = null
    private var rect: Rect = Rect()
    private var roundRect: RectF = RectF()
    private var parentRect: Rect = Rect()
    private var paint: Paint = Paint()
    private val defaultColor = 0xffdcdcdc.toInt()

    var listviewItemType: ItemParams? = null
    private val grayLands: MutableList<GrayLand> = mutableListOf()

    var loadingAnimtor: ILoadingAnimtor? = BaiJvGuoXiLoadingAnimtor(this)
//    var loadingAnimtor: ILoadingAnimtor? = RuoYinRuoXianLoadingAnimtor(this)

    init {
        paint.color = defaultColor
        setBackgroundColor(0xffffffff.toInt())
        loadingAnimtor?.setPaint(paint)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (grayLands.isEmpty()) {
            target?.let { drawViews(canvas, it) }
        } else {
            drawGrayLands(canvas)
        }
        loadingAnimtor?.onDraw(canvas)
    }

    private fun drawGrayLands(canvas: Canvas) {
        grayLands.forEach {
            it.draw(canvas, getAPaint())
        }
    }


    private fun drawViews(canvas: Canvas?, target: View) {
        if (target is ViewGroup) {
            if (target.background is GradientDrawable) {
                drawView(canvas, target)
            }
            if (target is AdapterView<*> || target is RecyclerView) {
                drawView(canvas, target)
            } else {
                for (i in 0 until target.childCount) {
                    drawViews(canvas, target.getChildAt(i))
                }
            }

        } else {
            if (target.javaClass.simpleName == "Space" || target.javaClass.simpleName == "View") {
//                return
            }
            drawView(canvas, target)
        }
    }

    private fun drawView(canvas: Canvas?, view: View) {
        canvas ?: return
        if (!isAValid(view)) {
            return
        }

        pad = if (view is ViewGroup) PAD_VIEWGROUP else PAD_VIEW

        rect.setEmpty()
        view.getGlobalVisibleRect(rect)
        parentRect.setEmpty()
        (parent as? View)?.getGlobalVisibleRect(parentRect)
        rect.top = rect.top - parentRect.top
        rect.bottom = rect.bottom - parentRect.top
        shirnkRect()

        if (view is AdapterView<*> || view is RecyclerView) {
            drawListView(view, canvas)
        } else {
            decideDrawView(view, canvas)
        }
    }

    private fun drawListView(view: View, canvas: Canvas) {
        SkUtil.debugView(view)
        grayLands.add(GrayLand(this).apply {
            landType = GrayLand.LAND_TYPE_ITEM
            itemRect = ItemRect(Rect(this@SkeletonView.rect), this@SkeletonView)
        })
    }

    private fun decideDrawView(view: View, canvas: Canvas): Boolean {
        SkUtil.debugView(view)
        var color = defaultColor
        when (view.background) {
            is GradientDrawable, is StateListDrawable -> {
                val d = view.background.constantState?.newDrawable()
                d?.colorFilter = PorterDuffColorFilter(0x88ffffff.toInt(), PorterDuff.Mode.SRC_ATOP)
                d?.bounds = rect
                grayLands.add(GrayLand(this).apply {
                    landType = GrayLand.LAND_TYPE_DRAWABLE
                    drawable = d
                })
                return false
            }

            is ColorDrawable -> {
                color = SkUtil.createNewColor((view.background as? ColorDrawable)?.color)
                    ?: defaultColor
            }
            null -> {
                if (view is TextView && (view.tag as? String)?.contains(TagValues.USE_TEXT_COLOR) == true) {
                    color = SkUtil.createNewColor((view as? TextView)?.currentTextColor) ?: defaultColor
                }
            }
        }
        grayLands.add(GrayLand(this).apply {
            landType = GrayLand.LAND_TYPE_RECT
            this.color = color
            rectF.left = roundRect.left
            rectF.top = roundRect.top
            rectF.right = roundRect.right
            rectF.bottom = roundRect.bottom
        })
        return true
    }

    private fun isAValid(view: View): Boolean {
        val size = view.measuredWidth != 0 && view.measuredHeight != 0 && view.visibility == VISIBLE
        return size && isViewInScreen(view)
    }

    private fun shirnkRect() {
        rect.left = rect.left + pad
        rect.right = rect.right - pad
        rect.top = rect.top + pad
        rect.bottom = rect.bottom - pad

        if (rect.right <= rect.left) {
            rect.right = rect.left + 2
        }
        if (rect.bottom <= rect.top) {
            rect.bottom = rect.top + 2
        }
        if (rect.left < 0 || rect.right < 0 || rect.top < 0 || rect.bottom < 0) {
            rect.setEmpty()
        }

        roundRect.apply {
            left = rect.left.toFloat()
            right = rect.right.toFloat()
            top = rect.top.toFloat()
            bottom = rect.bottom.toFloat()
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        loadingAnimtor?.cancel()
    }

    override fun onVisibilityChanged(changedView: View, visibility: Int) {
        super.onVisibilityChanged(changedView, visibility)
        if (changedView != this) {
            return
        }
        when (visibility) {
            INVISIBLE, GONE -> {
                loadingAnimtor?.cancel()
            }
            else -> {
                loadingAnimtor?.start()
            }
        }
    }

    override fun onWindowVisibilityChanged(visibility: Int) {
        super.onWindowVisibilityChanged(visibility)
        when (visibility) {
            INVISIBLE, GONE -> {
                loadingAnimtor?.cancel()
            }
            else -> {
                loadingAnimtor?.start()
            }
        }
    }


    fun getAPaint(): Paint {
        return paint
    }


    fun setPaintColor(color: Int) {
        val p = getAPaint()
        val a = p.alpha
        p.color = color
        p.alpha = a
    }

    fun setLoadingAnim(anim: ILoadingAnimtor) {
        loadingAnimtor?.cancel()
        loadingAnimtor = anim
    }

    private var display: DisplayMetrics? = null
    private fun isViewInScreen(view: View?): Boolean {
        view ?: return false
        if (display == null) {
            display = context.resources.displayMetrics
        }
        val loc = IntArray(2)
        view.getLocationOnScreen(loc)
        return loc[0] > (0 - view.measuredWidth) && loc[0] < (display?.widthPixels ?: 0)
                && loc[1] > (0 - view.measuredHeight) && loc[1] < (display?.heightPixels ?: 0)
    }

    fun isGrayLandReady(): Boolean {
        return grayLands.isNotEmpty()
    }

    private class GrayLand(val skeletonView: SkeletonView) {
        companion object {
            const val LAND_TYPE_RECT = 0
            const val LAND_TYPE_ITEM = 1
            const val LAND_TYPE_DRAWABLE = 2
        }

        var landType = LAND_TYPE_RECT

        val rectF = RectF()
        var radius = 10f
        var itemRect: ItemRect? = null
        var drawable: Drawable? = null
        var color: Int = 0

        fun draw(canvas: Canvas, paint: Paint) {
            skeletonView.setPaintColor(color)
            when (landType) {
                LAND_TYPE_RECT -> {
                    canvas.drawRoundRect(rectF, radius, radius, paint)
                }
                LAND_TYPE_ITEM -> {
                    itemRect?.draw(canvas)
                }
                LAND_TYPE_DRAWABLE -> {
                    drawable?.alpha = paint.alpha
                    drawable?.draw(canvas)
                }
            }
        }
    }
}